﻿
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Reflection.Emit;
using System.Reflection;
using System.Collections;
using System.ComponentModel;
using System.Text.RegularExpressions;

namespace DataDriven
{
    public static class MapperService
    {
        static event EventHandler QueryCachePurged;
        static Link<Type, Action<IDbCommand, bool>> bindByNameCache;
        static readonly Dictionary<Type, DbType> typeMap;
        static readonly MethodInfo enumParse;
        static readonly MethodInfo getItem;
        // static readonly DbProvider dbProvider;
        static MapperService()
        {
            typeMap = new Dictionary<Type, DbType>();
            typeMap[typeof(byte)] = DbType.Byte;
            typeMap[typeof(sbyte)] = DbType.SByte;
            typeMap[typeof(short)] = DbType.Int16;
            typeMap[typeof(ushort)] = DbType.UInt16;
            typeMap[typeof(int)] = DbType.Int32;
            typeMap[typeof(uint)] = DbType.UInt32;
            typeMap[typeof(long)] = DbType.Int64;
            typeMap[typeof(ulong)] = DbType.UInt64;
            typeMap[typeof(float)] = DbType.Single;
            typeMap[typeof(double)] = DbType.Double;
            typeMap[typeof(decimal)] = DbType.Decimal;
            typeMap[typeof(bool)] = DbType.Boolean;
            typeMap[typeof(string)] = DbType.String;
            typeMap[typeof(char)] = DbType.StringFixedLength;
            typeMap[typeof(Guid)] = DbType.Guid;
            typeMap[typeof(DateTime)] = DbType.DateTime;
            typeMap[typeof(DateTimeOffset)] = DbType.DateTimeOffset;
            typeMap[typeof(byte[])] = DbType.Binary;
            typeMap[typeof(byte?)] = DbType.Byte;
            typeMap[typeof(sbyte?)] = DbType.SByte;
            typeMap[typeof(short?)] = DbType.Int16;
            typeMap[typeof(ushort?)] = DbType.UInt16;
            typeMap[typeof(int?)] = DbType.Int32;
            typeMap[typeof(uint?)] = DbType.UInt32;
            typeMap[typeof(long?)] = DbType.Int64;
            typeMap[typeof(ulong?)] = DbType.UInt64;
            typeMap[typeof(float?)] = DbType.Single;
            typeMap[typeof(double?)] = DbType.Double;
            typeMap[typeof(decimal?)] = DbType.Decimal;
            typeMap[typeof(bool?)] = DbType.Boolean;
            typeMap[typeof(char?)] = DbType.StringFixedLength;
            typeMap[typeof(Guid?)] = DbType.Guid;
            typeMap[typeof(DateTime?)] = DbType.DateTime;
            typeMap[typeof(DateTimeOffset?)] = DbType.DateTimeOffset;

            enumParse = typeof(Enum).GetMethod("Parse", new Type[] { typeof(Type), typeof(string), typeof(bool) });

            PropertyInfo[] props = typeof(IDataRecord).GetProperties(BindingFlags.Instance | BindingFlags.Public);

            foreach (PropertyInfo prop in props)
            {
                var info = prop.GetIndexParameters();
                if (info != null && info.Length > 0 && info[0].ParameterType == typeof(int))
                {
                    getItem = prop.GetGetMethod();
                    break;
                }
            }

            ////dbProvider
            //dbProvider = new DbProvider()
            //{
            //    UseParameterPrefixInSql = true,
            //    UseParameterPrefixInParameter = true,
            //    ParameterPrefix = "@"
            //};
        }
        private static Action<IDbCommand, bool> GetBindByName(Type commandType)
        {
            if (commandType == null) return null; // GIGO
            Action<IDbCommand, bool> action;
            if (Link<Type, Action<IDbCommand, bool>>.TryGet(bindByNameCache, commandType, out action))
            {
                return action;
            }
            var prop = commandType.GetProperty("BindByName", BindingFlags.Public | BindingFlags.Instance);
            action = null;
            ParameterInfo[] indexers;
            MethodInfo setter;
            if (prop != null && prop.CanWrite && prop.PropertyType == typeof(bool)
                && ((indexers = prop.GetIndexParameters()) == null || indexers.Length == 0)
                && (setter = prop.GetSetMethod()) != null
                )
            {
                var method = new DynamicMethod(commandType.Name + "_BindByName", null, new Type[] { typeof(IDbCommand), typeof(bool) });
                var il = method.GetILGenerator();
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Castclass, commandType);
                il.Emit(OpCodes.Ldarg_1);
                il.EmitCall(OpCodes.Callvirt, setter, null);
                il.Emit(OpCodes.Ret);
                action = (Action<IDbCommand, bool>)method.CreateDelegate(typeof(Action<IDbCommand, bool>));
            }
            // cache it            
            Link<Type, Action<IDbCommand, bool>>.TryAdd(ref bindByNameCache, commandType, ref action);
            return action;
        }
        private static readonly Dictionary<Identity, CacheInfo> _queryCache = new Dictionary<Identity, CacheInfo>();
        private static void OnQueryCachePurged()
        {
            var handler = QueryCachePurged;
            if (handler != null) handler(null, EventArgs.Empty);
        }
        private static void SetQueryCache(Identity key, CacheInfo value)
        {
            lock (_queryCache) { _queryCache[key] = value; }
        }
        private static bool TryGetQueryCache(Identity key, out CacheInfo value)
        {
            lock (_queryCache) { return _queryCache.TryGetValue(key, out value); }
        }
        private static DbType LookupDbType(Type type, string name)
        {
            DbType dbType;
            var nullUnderlyingType = Nullable.GetUnderlyingType(type);
            if (nullUnderlyingType != null) type = nullUnderlyingType;
            if (type.IsEnum)
            {
                type = Enum.GetUnderlyingType(type);
            }
            if (typeMap.TryGetValue(type, out dbType))
            {
                return dbType;
            }
            if (typeof(IEnumerable).IsAssignableFrom(type))
            {
                return DbType.Xml;
            }
            throw new NotSupportedException(string.Format("The member {0} of type {1} cannot be used as a parameter value", name, type));
        }



        public static void PurgeQueryCache()
        {
            lock (_queryCache)
            {
                _queryCache.Clear();
            }
            OnQueryCachePurged();
        }




        public static int ExecuteInternal(IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType,string parameterPrefix)
        {
            Identity identity;
            CacheInfo info = null;

            bool isMulti = (param != null && (param is Array || param is IList));
            if (isMulti)
            {
                var multiExec = param as IEnumerable;
                bool isFirst = true;
                int total = 0;
                using (var cmd = SetupCommand(cnn, transaction, sql, null, null, commandTimeout, commandType, parameterPrefix))
                {

                    string masterSql = null;
                    foreach (var obj in multiExec)
                    {
                        if (isFirst)
                        {
                            masterSql = cmd.CommandText;
                            isFirst = false;
                            identity = new Identity(sql, cmd.CommandType, cnn, null, obj.GetType(), null);
                            info = GetCacheInfo(identity,parameterPrefix);
                        }
                        else
                        {
                            cmd.CommandText = masterSql;
                            cmd.Parameters.Clear();
                        }
                        info.ParamReader(cmd, obj);
                        total += cmd.ExecuteNonQuery();
                    }
                }
                return total;
            }
            else
            {
                identity = new Identity(sql, commandType, cnn, null, (object)param == null ? null : ((object)param).GetType(), null);
                info = GetCacheInfo(identity, parameterPrefix);
                using (var cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, (object)param, commandTimeout, commandType, parameterPrefix))
                {
                    #region 测试代码
                    //foreach (IDbDataParameter item in cmd.Parameters)
                    //{
                    //    string key = item.ParameterName;
                    //    object value = item.Value;
                    //}
                    #endregion
                    return cmd.ExecuteNonQuery();
                }
            }
        }

        public static int ExecuteSchemaSql(IDbConnection cnn, IDbTransaction transaction, List<string> tableSchemas,string parameterPrefix)
        {

            int total = 0;
            using (var cmd = SetupCommand(cnn, transaction, "", null, null, null, null, parameterPrefix))
            {
                foreach (string itemTableText in tableSchemas)
                {
                    cmd.CommandText = itemTableText;
                    //cmd.Parameters.Clear();
                    total += cmd.ExecuteNonQuery();
                }
            }
            return total;

        }

        public static T ScalarInternal<T>(IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType, string parameterPrefix)
        {
            Identity identity;
            CacheInfo info;
            Type type = typeof(T);
            identity = new Identity(sql, commandType, cnn, null, (object)param == null ? null : ((object)param).GetType(), null);
            info = GetCacheInfo(identity, parameterPrefix);
            using (var cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, (object)param, commandTimeout, commandType, parameterPrefix))
            {
                object obj = cmd.ExecuteScalar();
                if (obj == null || (obj is DBNull)) return default(T);
                else
                {
                    if (type.IsEnum)
                        obj = Convert.ChangeType(obj, typeof(int));
                    else
                        obj = Convert.ChangeType(obj, type);
                    return (T)obj;
                }
            }
        }


        public static T QueryModel<T>(IDbConnection cnn, string sql, object param, IDbTransaction tran, int? commandTimeout, CommandType? commandType,string parameterPrefix)
        {
            var identity = new Identity(sql, commandType, cnn, typeof(T), param == null ? null : param.GetType(), null);
            var info = GetCacheInfo(identity,parameterPrefix);

            using (var cmd = SetupCommand(cnn, tran, sql, info.ParamReader, param, commandTimeout, commandType,parameterPrefix))
            {
                using (var reader = cmd.ExecuteReader())
                {
                    Func<Func<IDataReader, object>> cacheDeserializer = delegate ()
                    {
                        info.Deserializer = GetDeserializer(typeof(T), reader, 0, -1, false);
                        SetQueryCache(identity, info);
                        return info.Deserializer;
                    };
                    if (info.Deserializer == null)
                    {
                        cacheDeserializer();
                    }
                    var deserializer = info.Deserializer;
                    object next = null;
                    while (reader.Read())
                    {
                        try
                        {
                            next = deserializer(reader);
                        }
                        catch (DataException)
                        {
                            deserializer = cacheDeserializer();
                            next = deserializer(reader);
                        }
                        break;
                    }
                    return (T)next;
                }
            }
        }


        public static IEnumerable<Dictionary<string, object>> QueryInternalFieldValue<T>(IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType,string parameterPrefix)
        {
            var identity = new Identity(sql, commandType, cnn, typeof(T), param == null ? null : param.GetType(), null);
            var info = GetCacheInfo(identity,parameterPrefix);

            using (var cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, param, commandTimeout, commandType,parameterPrefix))
            {
                using (var reader = cmd.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        for (int i = 0; i < reader.FieldCount; i++)
                        {
                            var dic = new Dictionary<string, object>();
                            if (reader.GetValue(i) != null && !string.IsNullOrEmpty(reader.GetValue(i).ToString()))
                            {
                                dic[reader.GetName(i)] = reader.GetValue(i);
                            }
                            else
                            {
                                dic[reader.GetName(i)] = null;
                            }
                            yield return dic;
                        }
                    }
                }
            }
        }

        public static IEnumerable<Dictionary<string, object>> QueryInternalKeyValue<T>(IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType,string parameterPrefix)
        {
            var identity = new Identity(sql, commandType, cnn, typeof(T), param == null ? null : param.GetType(), null);
            var info = GetCacheInfo(identity, parameterPrefix);

            using (var cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, param, commandTimeout, commandType, parameterPrefix))
            {
                using (var reader = cmd.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        var dic = new Dictionary<string, object>();
                        if (reader.GetValue(0) != null)
                        {
                            dic[reader.GetValue(0).ToString()] = reader.GetValue(1);
                        }
                        yield return dic;
                    }
                }
            }
        }

        public static DataTable QueryDataTable(IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType,string parameterPrefix)
        {
            var identity = new Identity(sql, commandType, cnn, null, param == null ? null : param.GetType(), null);
            var info = GetCacheInfo(identity, parameterPrefix);
            DataTable dataTable = new DataTable();
            using (var cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, param, commandTimeout, commandType, parameterPrefix))
            {
                //var dbcmd= cmd as System.Data.Common.DbCommand;

                //IDataAdapter pksda = new SqlDataAdapter(sql, conn);
                using (var reader = cmd.ExecuteReader())
                {
                    dataTable.Load(reader);
                }
            }
            return dataTable;
        }

        public static IEnumerable<T> QueryInternal<T>(IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType,string parameterPrefix)
        {
            var identity = new Identity(sql, commandType, cnn, typeof(T), param == null ? null : param.GetType(), null);
            var info = GetCacheInfo(identity, parameterPrefix);

            using (var cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, param, commandTimeout, commandType, parameterPrefix))
            {
                using (var reader = cmd.ExecuteReader())
                {
                    Func<Func<IDataReader, object>> cacheDeserializer = delegate ()
                    {
                        info.Deserializer = GetDeserializer(typeof(T), reader, 0, -1, false);
                        SetQueryCache(identity, info);
                        return info.Deserializer;
                    };

                    if (info.Deserializer == null)
                    {
                        cacheDeserializer();
                    }

                    var deserializer = info.Deserializer;

                    while (reader.Read())
                    {
                        object next;
                        try
                        {
                            next = deserializer(reader);
                        }
                        catch (DataException)
                        {
                            deserializer = cacheDeserializer();
                            next = deserializer(reader);
                        }
                        yield return (T)next;
                    }
                }
            }
        }
        public static IEnumerable<TReturn> Query<TFirst, TSecond, TReturn>(IDbConnection cnn, string sql, Func<TFirst, TSecond, TReturn> map, object param, bool buffered, IDbTransaction transaction, string splitOn, int? commandTimeout, CommandType? commandType, string parameterPrefix)
        {
            return MultiMap<TFirst, TSecond, DontMap, DontMap, DontMap, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType, parameterPrefix);
        }
        public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TReturn>(IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TReturn> map, object param, bool buffered, IDbTransaction transaction, string splitOn, int? commandTimeout, CommandType? commandType, string parameterPrefix)
        {
            return MultiMap<TFirst, TSecond, TThird, DontMap, DontMap, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType, parameterPrefix);
        }
        public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TReturn>(IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, object param, bool buffered, IDbTransaction transaction, string splitOn, int? commandTimeout, CommandType? commandType, string parameterPrefix)
        {
            return MultiMap<TFirst, TSecond, TThird, TFourth, DontMap, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType, parameterPrefix);
        }
        private static IEnumerable<TReturn> MultiMap<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(IDbConnection cnn, string sql, object map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType, string parameterPrefix)
        {
            var results = MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(cnn, sql, map, param, transaction, splitOn, commandTimeout, commandType, null, null, parameterPrefix);
            return buffered ? ToList(results) : results;
        }
        private static IEnumerable<TReturn> MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(IDbConnection cnn, string sql, object map, object param, IDbTransaction transaction, string splitOn, int? commandTimeout, CommandType? commandType, IDataReader reader, Identity identity,string parameterPrefix)
        {
            identity = identity ?? new Identity(sql, commandType, cnn, typeof(TFirst), (object)param == null ? null : ((object)param).GetType(), new[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth) });
            CacheInfo cinfo = GetCacheInfo(identity, parameterPrefix);
            IDbCommand ownedCommand = null;
            IDataReader ownedReader = null;

            try
            {
                if (reader == null)
                {
                    ownedCommand = SetupCommand(cnn, transaction, sql, cinfo.ParamReader, (object)param, commandTimeout, commandType, parameterPrefix);
                    ownedReader = ownedCommand.ExecuteReader();
                    reader = ownedReader;
                }
                Func<IDataReader, object> deserializer = null;
                Func<IDataReader, object>[] otherDeserializers = null;

                Action cacheDeserializers = delegate
                {
                    var deserializers = GenerateDeserializers(new Type[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth) }, splitOn, reader);
                    deserializer = cinfo.Deserializer = deserializers[0];
                    otherDeserializers = new Func<IDataReader, object>[deserializers.Length - 1];
                    for (int i = 0; i < otherDeserializers.Length; i++)
                    {
                        otherDeserializers[i] = deserializers[i + 1];
                    }
                    cinfo.OtherDeserializers = otherDeserializers;
                    SetQueryCache(identity, cinfo);
                };

                if ((deserializer = cinfo.Deserializer) == null || (otherDeserializers = cinfo.OtherDeserializers) == null)
                {
                    cacheDeserializers();
                }

                Func<IDataReader, TReturn> mapIt = GenerateMapper<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(deserializer, otherDeserializers, map);

                if (mapIt != null)
                {
                    while (reader.Read())
                    {
                        TReturn next;
                        try
                        {
                            next = mapIt(reader);
                        }
                        catch (DataException)
                        {
                            cacheDeserializers();
                            mapIt = GenerateMapper<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(deserializer, otherDeserializers, map);
                            next = mapIt(reader);
                        }
                        yield return next;
                    }
                }
            }
            finally
            {
                try
                {
                    if (ownedReader != null)
                    {
                        ownedReader.Dispose();
                    }
                }
                finally
                {
                    if (ownedCommand != null)
                    {
                        ownedCommand.Dispose();
                    }
                }
            }
        }
        private static IDbCommand SetupCommand(IDbConnection cnn, IDbTransaction transaction, string sql, Action<IDbCommand, object> paramReader, object obj, int? commandTimeout, CommandType? commandType, string parameterPrefix)
        {
            var cmd = cnn.CreateCommand();
            var bindByName = GetBindByName(cmd.GetType());
            if (bindByName != null) bindByName(cmd, true);
            cmd.Transaction = transaction;
            cmd.CommandText = FormatSql(sql, parameterPrefix);
            if (commandTimeout.HasValue)
                cmd.CommandTimeout = commandTimeout.Value;
            if (commandType.HasValue)
                cmd.CommandType = commandType.Value;
            if (paramReader != null)
            {
                paramReader(cmd, obj);
            }
            return cmd;
        }
        private static CacheInfo GetCacheInfo(Identity identity,string parameterPrefix)
        {
            CacheInfo info;
            if (!TryGetQueryCache(identity, out info))
            {
                info = new CacheInfo();
                if (identity.parametersType != null)
                {
                    if (IsSimpleValue(identity.parametersType))
                    {
                        info.ParamReader = delegate (IDbCommand cmd, object obj) 
                        { 
                            GetSimpleSerializer(cmd, identity, obj, parameterPrefix);
                        };
                    }
                    else if (typeof(IDictionary).IsAssignableFrom(identity.parametersType))
                    {
                        info.ParamReader = delegate (IDbCommand cmd, object obj) { GetDictionarySerializer(cmd, identity, obj, parameterPrefix); };
                    }
                    else if (identity.parametersType.IsClass)
                    {
                        info.ParamReader = GetClassSerializer(identity,parameterPrefix);
                    }
                }
                SetQueryCache(identity, info);
            }
            return info;
        }
        private static Func<IDataReader, object> GetDeserializer(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
        {
            Func<IDataReader, object> func = null;
            if (IsSimpleValue(type))
            {
                func = GetSimpleDeserializer(type, startBound);
            }
            else if (typeof(IDictionary).IsAssignableFrom(type))
            {
                func = GetDictionaryDeserializer(type, startBound);
            }
            else if (type.IsClass)
            {
                func = GetClassDeserializer(type, reader, startBound, length, returnNullIfFirstMissing);
            }
            return func;
        }




        //入
        private static Action<IDbCommand, object> GetClassSerializer(Identity identity,string parameterPrefix)
        {
            Type type = identity.parametersType;
            bool filterParams = identity.commandType.GetValueOrDefault(CommandType.Text) == CommandType.Text;

            var dm = new DynamicMethod(string.Format("ParamInfo{0}", Guid.NewGuid()), null, new[] { typeof(IDbCommand), typeof(object) }, type, true);

            var il = dm.GetILGenerator();

            il.DeclareLocal(type); // 0
            bool haveInt32Arg1 = false;
            il.Emit(OpCodes.Ldarg_1); // stack is now [untyped-param]
            il.Emit(OpCodes.Unbox_Any, type); // stack is now [typed-param]
            il.Emit(OpCodes.Stloc_0);// stack is now empty

            il.Emit(OpCodes.Ldarg_0); // stack is now [command]
            il.EmitCall(OpCodes.Callvirt, typeof(IDbCommand).GetProperty("Parameters").GetGetMethod(), null); // stack is now [parameters]

            IEnumerable<PropertyInfo> props = type.GetProperties();
            if (filterParams)
            {
                props = FilterParameters(props, identity.sql, parameterPrefix);
            }
            foreach (var prop in props)
            {
                if (filterParams)
                {
                    if (identity.sql.IndexOf(parameterPrefix + prop.Name, StringComparison.InvariantCultureIgnoreCase) < 0
                      && identity.sql.IndexOf("#" + prop.Name + "#", StringComparison.InvariantCultureIgnoreCase) < 0
                        )
                    { // can't see the parameter in the text (even in a comment, etc) - burn it with fire
                        continue;
                    }
                }

                DbType dbType = LookupDbType(prop.PropertyType, prop.Name);
                if (dbType == DbType.Xml)
                {
                    // this actually represents special handling for list types;
                    il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [command]
                    il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [command] [name]
                    il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [command] [name] [typed-param]
                    il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [command] [name] [typed-value]
                    if (prop.PropertyType.IsValueType)
                    {
                        il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [command] [name] [boxed-value]
                    }
                    il.EmitCall(OpCodes.Call, typeof(MapperService).GetMethod("PackListParameters"), null); // stack is [parameters]
                    continue;
                }
                il.Emit(OpCodes.Dup); // stack is now [parameters] [parameters]

                il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [parameters] [command]
                il.EmitCall(OpCodes.Callvirt, typeof(IDbCommand).GetMethod("CreateParameter"), null);// stack is now [parameters] [parameters] [parameter]

                il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]
                il.Emit(OpCodes.Ldstr, FormatNameForParameter(prop.Name, parameterPrefix)); // stack is now [parameters] [parameters] [parameter] [parameter] [name]
                il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("ParameterName").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter]

                il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]
                EmitInt32(il, (int)dbType);// stack is now [parameters] [parameters] [parameter] [parameter] [db-type]

                il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("DbType").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter]

                il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]
                EmitInt32(il, (int)ParameterDirection.Input);// stack is now [parameters] [parameters] [parameter] [parameter] [dir]
                il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Direction").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter]

                il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]
                il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [parameters] [parameter] [parameter] [typed-param]
                il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [parameters] [parameter] [parameter] [typed-value]
                bool checkForNull = true;
                if (prop.PropertyType.IsValueType)
                {
                    il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [parameters] [parameter] [parameter] [boxed-value]
                    if (Nullable.GetUnderlyingType(prop.PropertyType) == null)
                    {   // struct but not Nullable<T>; boxed value cannot be null
                        checkForNull = false;
                    }
                }
                if (checkForNull)
                {
                    if (dbType == DbType.String && !haveInt32Arg1)
                    {
                        il.DeclareLocal(typeof(int));
                        haveInt32Arg1 = true;
                    }
                    // relative stack: [boxed value]
                    il.Emit(OpCodes.Dup);// relative stack: [boxed value] [boxed value]
                    Label notNull = il.DefineLabel();
                    Label? allDone = dbType == DbType.String ? il.DefineLabel() : (Label?)null;
                    il.Emit(OpCodes.Brtrue_S, notNull);
                    // relative stack [boxed value = null]
                    il.Emit(OpCodes.Pop); // relative stack empty
                    il.Emit(OpCodes.Ldsfld, typeof(DBNull).GetField("Value")); // relative stack [DBNull]
                    if (dbType == DbType.String)
                    {
                        EmitInt32(il, 0);
                        il.Emit(OpCodes.Stloc_1);
                    }
                    if (allDone != null) il.Emit(OpCodes.Br_S, allDone.Value);
                    il.MarkLabel(notNull);
                    if (prop.PropertyType == typeof(string))
                    {
                        il.Emit(OpCodes.Dup); // [string] [string]
                        il.EmitCall(OpCodes.Callvirt, typeof(string).GetProperty("Length").GetGetMethod(), null); // [string] [length]
                        EmitInt32(il, 4000); // [string] [length] [4000]
                        il.Emit(OpCodes.Cgt); // [string] [0 or 1]
                        Label isLong = il.DefineLabel(), lenDone = il.DefineLabel();
                        il.Emit(OpCodes.Brtrue_S, isLong);
                        EmitInt32(il, 4000); // [string] [4000]
                        il.Emit(OpCodes.Br_S, lenDone);
                        il.MarkLabel(isLong);
                        EmitInt32(il, -1); // [string] [-1]
                        il.MarkLabel(lenDone);
                        il.Emit(OpCodes.Stloc_1); // [string] 
                    }
                    if (allDone != null) il.MarkLabel(allDone.Value);
                    // relative stack [boxed value or DBNull]
                }
                il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Value").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter]

                if (prop.PropertyType == typeof(string))
                {
                    var endOfSize = il.DefineLabel();
                    // don't set if 0
                    il.Emit(OpCodes.Ldloc_1); // [parameters] [parameters] [parameter] [size]
                    il.Emit(OpCodes.Brfalse_S, endOfSize); // [parameters] [parameters] [parameter]

                    il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]
                    il.Emit(OpCodes.Ldloc_1); // stack is now [parameters] [parameters] [parameter] [parameter] [size]
                    il.EmitCall(OpCodes.Callvirt, typeof(IDbDataParameter).GetProperty("Size").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter]

                    il.MarkLabel(endOfSize);
                }

                il.EmitCall(OpCodes.Callvirt, typeof(IList).GetMethod("Add"), null); // stack is now [parameters]
                il.Emit(OpCodes.Pop); // IList.Add returns the new index (int); we don't care
            }
            // stack is currently [command]
            il.Emit(OpCodes.Pop); // stack is now empty
            il.Emit(OpCodes.Ret);
            return (Action<IDbCommand, object>)dm.CreateDelegate(typeof(Action<IDbCommand, object>));
        }
        private static IEnumerable<PropertyInfo> FilterParameters(IEnumerable<PropertyInfo> parameters, string sql, string parameterPrefix)
        {
            List<PropertyInfo> ret = new List<PropertyInfo>();
            foreach (var para in parameters)
            {
                if (Regex.IsMatch(sql, parameterPrefix + para.Name + "([^a-zA-Z0-9_]+|$)", RegexOptions.IgnoreCase | RegexOptions.Multiline)
                  || Regex.IsMatch(sql, "#" + para.Name + "#", RegexOptions.IgnoreCase | RegexOptions.Multiline)
                    )
                {
                    ret.Add(para);
                }
            }
            return ret;
        }
        private static void GetSimpleSerializer(IDbCommand command, Identity identity, object obj,string parameterPrefix)
        {
            List<string> keys = GetKeys(identity);
            foreach (var item in keys)
            {
                AddParameter(command, item, obj, parameterPrefix);
            }
        }
        private static void GetDictionarySerializer(IDbCommand command, Identity identity, object obj, string parameterPrefix)
        {
            IDictionary value = obj as IDictionary;
            List<string> keys = GetKeys(identity);
            foreach (var item in keys)
            {
                if (Contains(value.Keys, item))
                    AddParameter(command, item, value[item], parameterPrefix);
            }
        }
        private static void AddParameter(IDbCommand command, string key, object value,string parameterPrefix)
        {
            var p = command.CreateParameter();
            p.ParameterName = FormatNameForParameter(key, parameterPrefix);
            if (value == null)
            {
                p.Value = DBNull.Value;
            }
            else
            {
                p.Value = value;
            }
            command.Parameters.Add(p);
        }
        private static List<string> GetKeys(Identity identity)
        {
            List<string> keys = new List<string>();
            string sql = identity.sql;
            Regex regex = new Regex("#([a-zA-Z0-9_]+?)#", RegexOptions.IgnoreCase | RegexOptions.Multiline);
            var ms = regex.Matches(sql);
            foreach (Match item in ms)
            {
                keys.Add(item.Groups[1].Value);

            }
            //第一种正则,这种正则匹配@参数1,@参数2，@参数3，匹配后结果是参数1，参数2，参数3,这种不支持IN所以不用这种，用第二种正则
            // regex = new Regex(dbProvider.ParameterPrefix + "([a-zA-Z0-9_]+?)([^a-zA-Z0-9_]+|$)", RegexOptions.IgnoreCase | RegexOptions.Multiline);
            //第二种正则,所有带@的参数都会匹配出来
            // regex = new Regex(@"[^@@](?<p>@\w+)", RegexOptions.IgnoreCase | RegexOptions.Multiline);
            regex = new Regex(@"[^@@](?<p>@\w+|#\w+|[:]\w+|[::]\w+|[?]\w+)", RegexOptions.IgnoreCase | RegexOptions.Multiline);
            ms = regex.Matches(sql);
            foreach (Match item in ms)
            {
                string tempStr = item.Groups[1].Value;//第二种正则匹配出来的带@，所以需要替换@为空字符串
                if (tempStr.StartsWith("@"))
                {
                    tempStr = tempStr.Replace("@", "");
                }
                if (tempStr.StartsWith(":"))
                {
                    tempStr = tempStr.Replace(":", "");
                }
                if (tempStr.StartsWith("::"))
                {
                    tempStr = tempStr.Replace("::", "");
                }
                if (tempStr.StartsWith("?"))
                {
                    tempStr = tempStr.Replace("?", "");
                }
                if (tempStr.StartsWith("#"))
                {
                    tempStr = tempStr.Replace("#", "");
                }
                keys.Add(tempStr);
            }
            return keys;
        }
        private static bool Contains(ICollection keys, string key)
        {
            foreach (var item in keys)
            {
                if (item.ToString() == key)
                    return true;
            }
            return false;
        }



        //出
        private static Func<IDataReader, TReturn> GenerateMapper<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(Func<IDataReader, object> deserializer, Func<IDataReader, object>[] otherDeserializers, object map)
        {
            switch (otherDeserializers.Length)
            {
                case 1:
                    return delegate (IDataReader r) { return ((Func<TFirst, TSecond, TReturn>)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r)); };
                case 2:
                    return delegate (IDataReader r) { return ((Func<TFirst, TSecond, TThird, TReturn>)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r)); };
                case 3:
                    return delegate (IDataReader r) { return ((Func<TFirst, TSecond, TThird, TFourth, TReturn>)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r), (TFourth)otherDeserializers[2](r)); };
                case 4:
                    return delegate (IDataReader r) { return ((Func<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>)map)((TFirst)deserializer(r), (TSecond)otherDeserializers[0](r), (TThird)otherDeserializers[1](r), (TFourth)otherDeserializers[2](r), (TFifth)otherDeserializers[3](r)); };
                default:
                    throw new NotSupportedException();
            }
        }
        private static Func<IDataReader, object>[] GenerateDeserializers(Type[] types, string splitOn, IDataReader reader)
        {
            int current = 0;
            var splits = splitOn.Split(',');
            var splitIndex = 0;

            Func<Type, int> nextSplit = delegate (Type type)
            {
                var currentSplit = splits[splitIndex];
                if (splits.Length > splitIndex + 1)
                {
                    splitIndex++;
                }
                bool skipFirst = true;
                int startingPos = current;
                int pos;
                for (pos = startingPos; pos < reader.FieldCount; pos++)
                {
                    if (splitOn == "*")
                    {
                        break;
                    }
                    if (string.Equals(reader.GetName(pos), currentSplit, StringComparison.OrdinalIgnoreCase))
                    {
                        if (skipFirst)
                        {
                            skipFirst = false;
                        }
                        else
                        {
                            break;
                        }
                    }
                }
                current = pos;
                return pos;
            };

            var deserializers = new List<Func<IDataReader, object>>();
            int split = 0;
            bool first = true;
            foreach (var type in types)
            {
                if (type != typeof(DontMap))
                {
                    int next = nextSplit(type);
                    deserializers.Add(GetDeserializer(type, reader, split, next - split, !first));
                    first = false;
                    split = next;
                }
            }

            return deserializers.ToArray();
        }
        private static Func<IDataReader, object> GetSimpleDeserializer(Type type, int index)
        {
            return delegate (IDataReader r)
            {
                object obj = r.GetValue(index);
                if (obj == null || (obj is DBNull)) return type.IsValueType ? Activator.CreateInstance(type) : null;
                else
                {
                    if (type.IsEnum)
                        obj = Convert.ChangeType(obj, typeof(int));
                    else
                        obj = Convert.ChangeType(obj, type);
                    return obj;
                }
            };
        }
        private static Func<IDataReader, object> GetDictionaryDeserializer(Type type, int index)
        {
            return delegate (IDataReader r)
            {
                IDictionary ht = Activator.CreateInstance(type) as IDictionary;
                for (int i = 0; i < r.FieldCount; i++)
                {
                    if (r[i] != null && !string.IsNullOrEmpty(r[i].ToString()))
                    {
                        ht?.Add(r.GetName(i), r[i]);
                    }
                    else
                    {
                        ht?.Add(r.GetName(i), null);
                    }
                }
                return ht;
            };
        }
        private static Func<IDataReader, object> GetClassDeserializer(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
        {
            var dm = new DynamicMethod(string.Format("Deserialize{0}", Guid.NewGuid()), type, new[] { typeof(IDataReader) }, true);

            var il = dm.GetILGenerator();
            il.DeclareLocal(typeof(int));
            il.DeclareLocal(type);
            bool haveEnumLocal = false;
            il.Emit(OpCodes.Ldc_I4_0);
            il.Emit(OpCodes.Stloc_0);
            List<PropertyInfo> properties = GetSettableProps(type);
            List<FieldInfo> fields = GetSettableFields(type);
            if (length == -1)
            {
                length = reader.FieldCount - startBound;
            }

            if (reader.FieldCount <= startBound)
            {
                throw new ArgumentException("When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id", "splitOn");
            }

            var names = new List<string>();

            for (int i = startBound; i < startBound + length; i++)
            {
                names.Add(reader.GetName(i));
            }
            var setters = new List<DynamicSetter>();

            foreach (var name in names)
            {
                PropertyInfo p = FirstOrDefault(properties, new Func<PropertyInfo, bool>(delegate (PropertyInfo pro) { return Equals(pro.Name, name, StringComparison.Ordinal); }))
                    ?? FirstOrDefault(properties, new Func<PropertyInfo, bool>(delegate (PropertyInfo pro) { return Equals(pro.Name, name, StringComparison.OrdinalIgnoreCase); }));
                FieldInfo f = FirstOrDefault(fields, new Func<FieldInfo, bool>(delegate (FieldInfo fin) { return Equals(fin.Name, name, StringComparison.Ordinal); }))
                    ?? FirstOrDefault(fields, new Func<FieldInfo, bool>(delegate (FieldInfo fin) { return Equals(fin.Name, name, StringComparison.OrdinalIgnoreCase); }));
                setters.Add(new DynamicSetter { Name = name, Property = p, Field = f });
            }

            int index = startBound;

            il.BeginExceptionBlock();
            // stack is empty
            il.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null)); // stack is now [target]
            bool first = true;
            var allDone = il.DefineLabel();
            foreach (var item in setters)
            {
                if (item.Property != null || item.Field != null)
                {
                    il.Emit(OpCodes.Dup); // stack is now [target][target]
                    Label isDbNullLabel = il.DefineLabel();
                    Label finishLabel = il.DefineLabel();

                    il.Emit(OpCodes.Ldarg_0); // stack is now [target][target][reader]
                    EmitInt32(il, index); // stack is now [target][target][reader][index]
                    il.Emit(OpCodes.Dup);// stack is now [target][target][reader][index][index]
                    il.Emit(OpCodes.Stloc_0);// stack is now [target][target][reader][index]
                    il.Emit(OpCodes.Callvirt, getItem); // stack is now [target][target][value-as-object]


                    Type memberType = item.Property != null ? item.Property.PropertyType : item.Field.FieldType;

                    if (memberType == typeof(char) || memberType == typeof(char?))
                    {
                        il.EmitCall(OpCodes.Call, typeof(MapperService).GetMethod(
                            memberType == typeof(char) ? "ReadChar" : "ReadNullableChar", BindingFlags.Static | BindingFlags.Public), null); // stack is now [target][target][typed-value]
                    }
                    else
                    {
                        il.Emit(OpCodes.Dup); // stack is now [target][target][value][value]
                        il.Emit(OpCodes.Isinst, typeof(DBNull)); // stack is now [target][target][value-as-object][DBNull or null]
                        il.Emit(OpCodes.Brtrue_S, isDbNullLabel); // stack is now [target][target][value-as-object]

                        // unbox nullable enums as the primitive, i.e. byte etc

                        var nullUnderlyingType = Nullable.GetUnderlyingType(memberType);
                        var unboxType = nullUnderlyingType != null && nullUnderlyingType.IsEnum ? nullUnderlyingType : memberType;

                        if (unboxType.IsEnum)
                        {
                            if (!haveEnumLocal)
                            {
                                il.DeclareLocal(typeof(string));
                                haveEnumLocal = true;
                            }

                            Label isNotString = il.DefineLabel();
                            il.Emit(OpCodes.Dup); // stack is now [target][target][value][value]
                            il.Emit(OpCodes.Isinst, typeof(string)); // stack is now [target][target][value-as-object][string or null]
                            il.Emit(OpCodes.Dup);// stack is now [target][target][value-as-object][string or null][string or null]
                            il.Emit(OpCodes.Stloc_2); // stack is now [target][target][value-as-object][string or null]
                            il.Emit(OpCodes.Brfalse_S, isNotString); // stack is now [target][target][value-as-object]

                            il.Emit(OpCodes.Pop); // stack is now [target][target]


                            il.Emit(OpCodes.Ldtoken, unboxType); // stack is now [target][target][enum-type-token]
                            il.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);// stack is now [target][target][enum-type]
                            il.Emit(OpCodes.Ldloc_2); // stack is now [target][target][enum-type][string]
                            il.Emit(OpCodes.Ldc_I4_1); // stack is now [target][target][enum-type][string][true]
                            il.EmitCall(OpCodes.Call, enumParse, null); // stack is now [target][target][enum-as-object]

                            il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]

                            if (nullUnderlyingType != null)
                            {
                                il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType }));
                            }
                            if (item.Property != null)
                            {
                                var setter = item.Property.DeclaringType == type ? item.Property.GetSetMethod(true) : item.Property.DeclaringType.GetProperty(item.Property.Name).GetSetMethod(true);
                                il.Emit(OpCodes.Callvirt, setter); // stack is now [target]
                            }
                            else
                            {
                                il.Emit(OpCodes.Stfld, item.Field); // stack is now [target]
                            }
                            il.Emit(OpCodes.Br_S, finishLabel);


                            il.MarkLabel(isNotString);
                        }
                        //if (memberType == typeof(System.Data.Linq.Binary))
                        //{
                        //    il.Emit(OpCodes.Unbox_Any, typeof(byte[])); // stack is now [target][target][byte-array]
                        //    il.Emit(OpCodes.Newobj, typeof(System.Data.Linq.Binary).GetConstructor(new Type[] { typeof(byte[]) }));// stack is now [target][target][binary]
                        //}
                        //else
                        //{
                        il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]
                        //}
                        if (nullUnderlyingType != null && nullUnderlyingType.IsEnum)
                        {
                            il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType }));
                        }
                    }
                    if (item.Property != null)
                    {
                        var setter = item.Property.DeclaringType == type ? item.Property.GetSetMethod(true) : item.Property.DeclaringType.GetProperty(item.Property.Name).GetSetMethod(true);
                        il.Emit(OpCodes.Callvirt, setter); // stack is now [target]
                    }
                    else
                    {
                        il.Emit(OpCodes.Stfld, item.Field); // stack is now [target]
                    }

                    il.Emit(OpCodes.Br_S, finishLabel); // stack is now [target]

                    il.MarkLabel(isDbNullLabel); // incoming stack: [target][target][value]

                    il.Emit(OpCodes.Pop); // stack is now [target][target]
                    il.Emit(OpCodes.Pop); // stack is now [target]

                    if (first && returnNullIfFirstMissing)
                    {
                        il.Emit(OpCodes.Pop);
                        il.Emit(OpCodes.Ldnull); // stack is now [null]
                        il.Emit(OpCodes.Stloc_1);
                        il.Emit(OpCodes.Br, allDone);
                    }

                    il.MarkLabel(finishLabel);
                }
                first = false;
                index += 1;
            }
            il.Emit(OpCodes.Stloc_1); // stack is empty
            il.MarkLabel(allDone);
            il.BeginCatchBlock(typeof(Exception)); // stack is Exception
            il.Emit(OpCodes.Ldloc_0); // stack is Exception, index
            il.Emit(OpCodes.Ldarg_0); // stack is Exception, index, reader
            il.EmitCall(OpCodes.Call, typeof(MapperService).GetMethod("ThrowDataException"), null);
            il.Emit(OpCodes.Ldnull);
            il.Emit(OpCodes.Stloc_1); // to make it verifiable
            il.EndExceptionBlock();

            il.Emit(OpCodes.Ldloc_1); // stack is empty
            il.Emit(OpCodes.Ret);

            return (Func<IDataReader, object>)dm.CreateDelegate(typeof(Func<IDataReader, object>));
        }
        private static List<PropertyInfo> GetSettableProps(Type t)
        {
            List<PropertyInfo> ret = new List<PropertyInfo>();
            PropertyInfo[] props = t.GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (PropertyInfo p in props)
            {
                MethodInfo Setter = p.DeclaringType == t ? p.GetSetMethod(true) : p.DeclaringType.GetProperty(p.Name).GetSetMethod(true);
                if (Setter != null)
                {
                    ret.Add(p);
                }
            }
            return ret;
        }
        private static List<FieldInfo> GetSettableFields(Type t)
        {
            List<FieldInfo> ret = new List<FieldInfo>();
            ret.AddRange(t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance));
            return ret;
        }
        private static TSource FirstOrDefault<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            if (predicate == null)
            {
                throw new ArgumentNullException("predicate");
            }
            foreach (TSource local in source)
            {
                if (predicate(local))
                {
                    return local;
                }
            }
            return default(TSource);
        }
        private static bool Equals(string a, string b, StringComparison comparisonType)
        {
            return string.Compare(a, b, comparisonType) == 0;
        }








        private static void EmitInt32(ILGenerator il, int value)
        {
            switch (value)
            {
                case -1: il.Emit(OpCodes.Ldc_I4_M1); break;
                case 0: il.Emit(OpCodes.Ldc_I4_0); break;
                case 1: il.Emit(OpCodes.Ldc_I4_1); break;
                case 2: il.Emit(OpCodes.Ldc_I4_2); break;
                case 3: il.Emit(OpCodes.Ldc_I4_3); break;
                case 4: il.Emit(OpCodes.Ldc_I4_4); break;
                case 5: il.Emit(OpCodes.Ldc_I4_5); break;
                case 6: il.Emit(OpCodes.Ldc_I4_6); break;
                case 7: il.Emit(OpCodes.Ldc_I4_7); break;
                case 8: il.Emit(OpCodes.Ldc_I4_8); break;
                default:
                    if (value >= -128 && value <= 127)
                    {
                        il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
                    }
                    else
                    {
                        il.Emit(OpCodes.Ldc_I4, value);
                    }
                    break;
            }
        }
        private static List<T> ToList<T>(IEnumerable<T> data)
        {
            List<T> ret = new List<T>();
            foreach (T t in data)
            {
                ret.Add(t);
            }
            return ret;
        }
        private static bool IsSimpleValue(Type type)
        {
            if (
                        type.IsEnum ||
                        isSame(type, typeof(byte)) || isSame(type, typeof(byte?)) ||
                        isSame(type, typeof(sbyte)) || isSame(type, typeof(sbyte?)) ||
                        isSame(type, typeof(long)) || isSame(type, typeof(long?)) ||
                        isSame(type, typeof(ulong)) || isSame(type, typeof(ulong?)) ||
                        isSame(type, typeof(short)) || isSame(type, typeof(short?)) ||
                        isSame(type, typeof(ushort)) || isSame(type, typeof(ushort?)) ||
                        isSame(type, typeof(int)) || isSame(type, typeof(int?)) ||
                        isSame(type, typeof(uint)) || isSame(type, typeof(uint?)) ||
                        isSame(type, typeof(float)) || isSame(type, typeof(float?)) ||
                        isSame(type, typeof(double)) || isSame(type, typeof(double?)) ||
                        isSame(type, typeof(decimal)) || isSame(type, typeof(decimal?)) ||
                        isSame(type, typeof(char)) || isSame(type, typeof(char?)) ||
                        isSame(type, typeof(bool)) || isSame(type, typeof(bool?)) ||
                        isSame(type, typeof(DateTime)) || isSame(type, typeof(DateTime?)) ||
                        isSame(type, typeof(string)) || isSame(type, typeof(object))
            )
                return true;
            else
                return false;
        }
        private static bool isSame(Type type1, Type type2)
        {
            if (type1 == type2) return true;
            else return false;
        }


        //后来加的方法
        private static string FormatNameForSql(string parameterName, string parameterPrefix)
        {
            //return dbProvider.UseParameterPrefixInSql ? (dbProvider.ParameterPrefix + parameterName) : parameterName;
            return parameterPrefix + parameterName;
        }
        private static string FormatNameForParameter(string parameterName, string parameterPrefix)
        {
            // return dbProvider.UseParameterPrefixInParameter ? (dbProvider.ParameterPrefix + parameterName) : parameterName;
            return parameterPrefix + parameterName;
        }
        private static string FormatSql(string sql,string parameterPrefix)
        {
            Regex regex = new Regex("#([a-zA-Z0-9_]+?)#", RegexOptions.IgnoreCase | RegexOptions.Multiline);
            var ms = regex.Matches(sql);
            foreach (Match item in ms)
            {
                sql = sql.Replace(item.Value, FormatNameForSql(item.Groups[1].Value, parameterPrefix));
            }
            return sql;
        }




        //以下方法仅内部使用（Emit使用）
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public static char ReadChar(object value)
        {
            if (value == null || value is DBNull) return default(char);
            else return Convert.ToChar(value);
        }
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public static char? ReadNullableChar(object value)
        {
            if (value == null || value is DBNull) return null;
            else return Convert.ToChar(value);
        }
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public static void ThrowDataException(Exception ex, int index, IDataReader reader)
        {
            string name = "null", value = "null";
            if (reader != null && index >= 0 && index < reader.FieldCount)
            {
                name = reader.GetName(index);
                object val = reader.GetValue(index);
                if (val == null || val is DBNull)
                {
                    value = "null";
                }
                else
                {
                    value = val.ToString();
                }
            }
            throw new DataException(string.Format("Error parsing column {0} ({1}={2})", index, name, value), ex);
        }
    }
}